/* $Id: Statistics.java,v 1.48 2010/06/08 06:40:36 nhnb Exp $ */
/***************************************************************************
 *                      (C) Copyright 2003 - Marauroa                      *
 ***************************************************************************
 ***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/
package marauroa.server.game;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import marauroa.common.Configuration;
import marauroa.common.Log4J;
import marauroa.server.db.command.DBCommandQueue;
import marauroa.server.game.dbcommand.LogStatisticsCommand;

/**
 * This class encapsulate everything related to the statistics recollection and
 * storage.
 */
public class SimulationStatsClient /*implements StatisticsMBean*/ {

	/**
	 *  time between statistics dumps
	 */
	public static final int DATABASE_STATISTICS_LAPSUS = 60000;

	/** the logger instance. */
	private static final marauroa.common.Logger logger = Log4J.getLogger(SimulationStatsClient.class);
        private PrintWriter outputFile;

	/**
	 * This class is very similar to a Map<String, Long> with the extra that
	 * adds some comodity methods like:
	 * <ul>
	 * <li>add
	 * <li>print
	 * </ul>
	 *
	 * @author miguel
	 */
	public static class Variables implements Iterable<String>, Cloneable {

		private Map<String, String> content;
                private boolean first=true;

		/**
		 * Constructor
		 */
		public Variables() {
			content = Collections.synchronizedMap(new HashMap<String, String>());
                        first = true;
		}

		/**
		 * Clear all the variables
		 */
		public void clear() {
			content.clear();
		}

		/**
		 * Put a new variable at the Map
		 *
		 * @param type
		 *            name of the variable
		 * @param value
		 *            its value
		 */
		public synchronized void put(String type, String value) {
			content.put(type, value);
		}

		/**
		 * Add value to previously existing variable.
		 *
		 * @param type
		 *            name of the variable
		 * @param value
		 *            value to add
		 */
		public synchronized void add(String type, String value) {
                        if(content.get(type)!=null && type.compareToIgnoreCase("ID")==0){
                            int t = Integer.parseInt(content.get(type))+Integer.parseInt(value);
                            put(type,""+t);
                        }
                        else
                            put(type, value);
		}

		/**
		 * Returns a variable value
		 *
		 * @param type
		 *            name of the variable
		 * @return its value
		 */
		public String get(String type) {
			return content.get(type);
		}

		/**
		 * Iterate over the variables
		 *
		 * @return an iterator over the variables
		 */
		public Iterator<String> iterator() {
			return content.keySet().iterator();
		}

		/**
		 * Adds to this instance the instance var
		 *
		 * @param var
		 *            a instance of Variables to add to this one.
		 */
		public void add(Variables var) {
			for (String type : var) {
				add(type, var.get(type));
			}
		}

		/**
		 * Prints the variable
		 *
		 * @param out the writer to write to
		 * @param diff ignored
		 */
		public synchronized  void print(PrintWriter out, @SuppressWarnings("unused") double diff) {
                        if (first){
                            initPrint(out);
                        }
			synchronized (content) {
				out.println();
                                for (String type : content.keySet()) {
					out.print(content.get(type)+", ");
				}
                                
			}
		}

                public void initPrint(PrintWriter out) {
			synchronized (content) {
                                content.put("time_stamp", "0");
				for (String type : content.keySet()) {
					out.print(type+ ", ");
				}
			}
                        first = false;
		}
		

		@Override
		public Object clone(){
			try {
				Variables var = (Variables) super.clone();
				synchronized (this.content) {
					var.content = Collections.synchronizedMap(new HashMap<String, String>(this.content));
				}
				return var;
			} catch (CloneNotSupportedException e) {
				logger.error(e, e);
				return null;
			}
		}
	}

	/** This is the actual values */
	private Variables now;

	/** This is variables values since the server startup */
	private Variables sinceStart;

	/** Server start time */
	private long startTime;

	private SimulationStatsClient() {
		startTime = System.currentTimeMillis();
		now = new Variables();
		sinceStart = new Variables();
                try{
                    Configuration conf = Configuration.getConfiguration();
                    String filename = conf.get("simulationclient_filename");
                    if (filename == null) {
                            return;
                    }
                    filename=conf.get("zone_size")+"/"+conf.get("zone_size") +"_"+System.currentTimeMillis()+filename;
                    outputFile = new PrintWriter(new FileOutputStream(filename, true));
                } catch (IOException exception){
                    exception.printStackTrace();
                }
                //sinceStart.initPrint(outputFile);
                now.add("ID", "0");
                sinceStart.add("ID", "0");
		init();
	}

	private synchronized void init() {
		/** we need these for JDBC Database */				
                now.add("player_name", "0");
                now.add("Perception Type", "-1");                		
                now.add("client_id", "-1");                
                now.add("client_response", "0");
                sinceStart.add("player_name", "0");
                sinceStart.add("Perception Type", "-1");                		
                sinceStart.add("client_id", "-1");                
                sinceStart.add("client_response", "0");
                
	}

	private static SimulationStatsClient stats;

	/**
	 * Returns an unique instance of Statistics. This is a singleton.
	 *
	 * @return a statistics object
	 */
	public static SimulationStatsClient getStatistics() {
		if (stats == null) {
			stats = new SimulationStatsClient();
		}

		return stats;
	}

	/**
	 * Sets an attribute
	 *
	 * @param type
	 *            attribute name
	 * @param value
	 *            its value
	 */
	public synchronized void set(String type, String value) {
		now.put(type, value);
		sinceStart.put(type, value);
	}

	/**
	 * Adds an attribute to its existing
	 *
	 * @param type
	 * @param value
	 */
	public synchronized void add(HashMap<String, String> map) {
                for (String key: map.keySet()){
                    now.add(key, map.get(key));
                    sinceStart.add(key, map.get(key));
                }
                print();
	}

	/**
	 * Return the value of an attribute since the server start. This method is
	 * used by the Bean interface.
	 *
	 * @param type
	 *            the attribute name
	 */
	public String get(String type) {
		return sinceStart == null ? "-1" : sinceStart.get(type);
	}
        
	/**
	 * Print to $statistics_filename file the content of the statistics object.
	 */
	public synchronized void print() {
		try {
			long currentTime = System.currentTimeMillis();

			double diff = (currentTime - startTime) / 1000.0;
			//outputFile.println("statistics time=" + (currentTime / 1000));
			//outputFile.println("uptime value=" + diff );

			long totalMemory = Runtime.getRuntime().totalMemory() / 1024;
			long usedMemory = totalMemory - (Runtime.getRuntime().freeMemory() / 1024);

			//outputFile.println("memory total=" + totalMemory + " used=" + usedMemory);
			//logger.info("Total/Used memory: " + totalMemory + "/" + usedMemory);

			sinceStart.print(outputFile, diff);
                        outputFile.flush();
                        init();
			//out.println("</statistics>");
			//outputFile.close();
		} catch (Exception e) {
			logger.warn("error while printing statistics to file configured in parameter \"statistics_filename\". ", e);
		}
	}

    public PrintWriter getOutputFile() {
        return outputFile;
    }

	

}
